import os
import time
import socket
import struct
import select
import statistics
import sys


def checksum(data):
    """
    计算ICMP校验和
    """
    sum = 0
    # 处理16位对齐的数据
    for i in range(0, len(data), 2):
        if i + 1 < len(data):
            word = (data[i] << 8) + data[i + 1]
            sum += word
    # 处理奇数长度的情况
    if len(data) % 2:
        sum += data[-1] << 8
    # 将高16位加到低16位
    sum = (sum >> 16) + (sum & 0xFFFF)
    sum += sum >> 16
    # 取反
    return ~sum & 0xFFFF


def ping_python(host, count=4, timeout=2):
    """
    纯Python实现的ping功能
    参数:
        host: 要ping的主机名或IP地址
        count: 发送的ping请求次数(默认为4)
        timeout: 超时时间(秒，默认为2)
    返回:
        (是否可达, 平均延迟ms) 如果不可达则延迟为None
    """
    try:
        dest_ip = socket.gethostbyname(host)
    except socket.gaierror:
        return (False, None)

    # 创建原始套接字(需要管理员/root权限)
    try:
        icmp_socket = socket.socket(
            socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP
        )
    except PermissionError:
        print("需要管理员/root权限来创建原始套接字")
        return (False, None)

    # 设置超时
    icmp_socket.settimeout(timeout)

    packet_id = os.getpid() & 0xFFFF
    latencies = []

    for seq in range(count):
        # 构建ICMP包
        header = struct.pack("!BBHHH", 8, 0, 0, packet_id, seq)
        data = struct.pack("!d", time.time())  # 使用当前时间作为数据

        # 计算校验和
        chksum = checksum(header + data)
        header = struct.pack("!BBHHH", 8, 0, chksum, packet_id, seq)
        packet = header + data

        # 发送包
        start_time = time.time()
        icmp_socket.sendto(packet, (dest_ip, 1))

        # 等待响应
        try:
            ready = select.select([icmp_socket], [], [], timeout)
            if ready[0]:
                recv_packet, addr = icmp_socket.recvfrom(1024)
                end_time = time.time()

                # 解析响应包
                icmp_header = recv_packet[20:28]
                type, code, chksum, p_id, sequence = struct.unpack(
                    "!BBHHH", icmp_header
                )

                if p_id == packet_id and sequence == seq:
                    latency = (end_time - start_time) * 1000  # 转换为毫秒
                    latencies.append(latency)
                    print(f"icmp seq={seq}, latency={latency:.2f}ms")
                time.sleep(1)
            else:
                continue  # 超时，继续下一个ping
        except socket.timeout:
            continue

    icmp_socket.close()

    if latencies:
        return (True, statistics.mean(latencies))
    else:
        return (False, None)

if __name__ == "__main__":
    # 示例使用
    host = "www.baidu.com"
    if len(sys.argv) > 1:
        host = sys.argv[1]
    reachable, latency = ping_python(host)
    if reachable:
        print(f"主机 {host} 可达，平均延迟: {latency:.2f}ms")
    else:
        print(f"主机 {host} 不可达")
